package cn.honqun.tinycms.common.code;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import lombok.Builder;
import lombok.Data;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.NonNull;
import org.yaml.snakeyaml.Yaml;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.sql.Types;
import java.util.Collections;
import java.util.Iterator;

public class CodeGenerator {
    //代码存放目录
    private static final String SOURCE_FOLDER = "H://codes";

    public static void main(String[] args) {
        new CodeGenerator().generate("t_role");
    }

    /**
     * 生成代码
     * @param tables 表名列表
     */
    public void generate(String... tables) {
        DS ds = getDS(); //读取application.yml中的数据源配置

        FastAutoGenerator.create(ds.getUrl(), ds.getUsername(), ds.getPassword())
                .globalConfig(builder -> {
                    builder.author("catstiger") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .enableSpringdoc()
                            .outputDir(SOURCE_FOLDER); // 指定输出目录
                })
                .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    // 自定义类型转换
                    if (typeCode == Types.TINYINT) {
                        return DbColumnType.BOOLEAN;
                    } else if (typeCode == Types.SMALLINT) {
                        return DbColumnType.INTEGER;
                    } else if (typeCode == Types.DATE || typeCode == Types.TIMESTAMP) {
                        return DbColumnType.DATE;
                    }

                    return typeRegistry.getColumnType(metaInfo);

                }))
                .packageConfig(builder -> {
                    builder.parent("cn.honqun") // 设置父包名
                            .moduleName("tinycms") // 设置父包模块名
                            .mapper("repository") //Mapper包名
                            .entity("domain.entity")
                            .pathInfo(Collections.singletonMap(OutputFile.xml, SOURCE_FOLDER)); // 设置mapperXml生成路径
                })

                .strategyConfig(builder -> {
                    builder.addInclude(tables)// 设置需要生成的表名
                            .addTablePrefix("t_"); // 设置过滤表前缀
                    //实体类
                    builder.entityBuilder().enableLombok().enableTableFieldAnnotation().enableFileOverride();
                    //mybatis映射
                    builder.mapperBuilder().enableFileOverride();
                    //服务类
                    builder.serviceBuilder().formatServiceFileName("%sService").enableFileOverride();
                    //控制类
                    builder.controllerBuilder().enableRestStyle().enableFileOverride();

                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .execute();
    }

    /**
     * 从application.yml中读取数据源
     */
    @NonNull
    private DS getDS() {
        Yaml yaml = new Yaml();
        Resource resource = new ClassPathResource("application.yml");
        DS ds = null;
        try (Reader reader = new FileReader(resource.getFile())) {
            for (Iterator<Object> itr = yaml.loadAll(reader).iterator(); itr.hasNext(); ) {
                JSONObject jsonObject = JSONUtil.parseObj(itr.next());

                String driverPath = "spring.datasource.driver-class-name";
                String urlPath = "spring.datasource.url";
                String usernamePath = "spring.datasource.username";
                String passwordPath = "spring.datasource.password";

                ds = DS.builder()
                        .driver((String) jsonObject.getByPath(driverPath))
                        .url((String) jsonObject.getByPath(urlPath))
                        .username((String) jsonObject.getByPath(usernamePath))
                        .password((String) jsonObject.getByPath(passwordPath))
                        .build();
                break;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        assert ds != null;
        return ds;
    }

    /**
     * 数据源的数据结构
     */
    @Data
    @Builder
    private static class DS {
        private String driver;
        private String url;
        private String username;
        private String password;
    }
}